#include <string>
#include <assert.h>
#include <iostream>
#include "../list.hpp"
#include "../hashTable.hpp"
#include "../game.hpp"
#include "test_utilities.hpp"
#include "hashTable_test.hpp"

using std::cout;
using std::endl;
using std::string;
using std::exception;

namespace hashTableTest{

class TESTER_NOT_IMPLEMENTED : public exception{};

const ID GAME_ID = 200;
const ID COMPANY_ID = 1;
const int RATE = 5;
const int N=10;

void testInsert();
void testRemove();
void testFind();
void testMember(); 


void run_hashTable_tests () {
    RUN_TEST(testInsert);
    RUN_TEST(testRemove);
    RUN_TEST(testFind);
	return;
}

/*
Scenario:
1.
    insert 1
    insert 1 - error
    for i in increments of 10:
        insert {1..i} - remove {1..i}
    
 */
void testInsert(){
    Game game1(GAME_ID, COMPANY_ID, RATE);
    HashTable table1(N);
    ASSERT_NO_THROW(table1.insert(game1));
    ASSERT_THROWS(ElementAlreadyInTableException, table1.insert(game1));
    for (int i = 1; i <= 101; i+=10){
        for (int j = -i; j < i; ++j){
            Game tempGame(j, abs(j)%N, RATE);
            ASSERT_NO_THROW(table1.insert(tempGame));
        }
        for (int j = -i; j < i; ++j){
            ASSERT_NO_THROW(table1.remove(j));
        }
    }
    return;
}


/*
Scenario:
1. 
    remove non existent object - error
    insert 1
    remove 2 - error
    remove 1
    remove 1 - error
 */
void testRemove(){
    Game game1(GAME_ID, COMPANY_ID, RATE);
    Game game2(GAME_ID+1, COMPANY_ID, RATE);
    HashTable table1(N);
    ASSERT_THROWS(ElementNotInTableException, table1.remove(game1.gameId));
    ASSERT_NO_THROW(table1.insert(game1));
    ASSERT_THROWS(ElementNotInTableException, table1.remove(game2.gameId));
    ASSERT_NO_THROW(table1.remove(game1.gameId));
    ASSERT_THROWS(ElementNotInTableException, table1.remove(game1.gameId));
}

/*
Scenario:
1. 
    find 50 - error    
    insert {1..1000}
    find 50
    remove 1,3,5...999 
    find 50
    remove 50 
    find 50 - error
    remove 2,4,6...1000
    insert {1..10}
    insert 50 
    find 50 

 */
void testFind(){
    Game game1(GAME_ID+200, COMPANY_ID, RATE);
    Game dummyGame(GAME_ID+200, 0, 0);
    HashTable table1(N);

    ASSERT_EQUALS(NULL, table1.find(game1.gameId));
    
    for (int i = 0; i < 1000; ++i)
    {
        Game tempGame(i, COMPANY_ID, RATE);
        ASSERT_NO_THROW(table1.insert(tempGame));    
    }
    const Game* res=NULL;
    ASSERT_NO_THROW(res = table1.find(game1.gameId));
    ASSERT_TRUE(res->getId() == game1.gameId);
    
    for (int gameId = 1; gameId < 1000; gameId+=2)
    {
        ASSERT_NO_THROW(table1.remove(gameId));   //remove odd-numbered games
    }
    ASSERT_NO_THROW(table1.find(game1.gameId));
    
    table1.remove(game1.gameId);
    ASSERT_EQUALS(NULL, table1.find(game1.gameId));

    ASSERT_NO_THROW(table1.insert(game1));
    for (int gameId = 0; gameId < 1000; gameId+=2)
    {
        Game tempGame(gameId, 0, 0);
        ASSERT_NO_THROW(table1.remove(gameId));   //remove even-numbered games (empty hash)
    }

    for (int i = 0; i < 10; i+=1)
    {
        Game tempGame(i, COMPANY_ID, RATE);
        ASSERT_NO_THROW(table1.insert(tempGame));  
    }
        ASSERT_NO_THROW(table1.insert(game1));  
        ASSERT_NO_THROW(res = table1.find(game1.gameId));  
        ASSERT_TRUE(res->gameId == game1.gameId);
}

}//End namespace hashTableTest
